// ============================================================================
// JigSocket
// Wraps the standard Java socket class in a way which makes it easy for
// Jiggle to use. Basically hide the details of the socket away from
// the general code. A JigSocket knows when it has lost the connection
// and just does nothing in this instance. This allows the majority of
// the code to just assume everything is fine, the lost connection can
// then be handled in one place.
//
// CLH April-June 1998
// ============================================================================

package com.chrishobson.jiggle;

import java.io.*;
import java.net.*;

public class JigSocket {
  // ==========================================================================
  // Creates a JigSocket to talk to a client via socket 's'
  // ==========================================================================
  public JigSocket(Socket s) {
    m_sock = s;
    m_ok = true;

    try {
      // Set the time out to 5 milliseconds so that the client doesn't
      // wait forever for some data that may never arrive
      m_sock.setSoTimeout(5);
      // Create the reading and writing streams as recommend in the
      // JDK online help
      m_out = new PrintWriter(m_sock.getOutputStream());
      m_in = new BufferedReader(new InputStreamReader(m_sock.getInputStream()));
    } catch (Exception e) {
      // If anything goes wrong then all bets are off
      m_ok = false;
    }
  }

  // ==========================================================================
  // Send a string down the socket. Do nothing if the connection is lost
  // ==========================================================================
  public void send(String s) {
    // Do nothing is the socket is not OK
    if (m_ok) {
      try {
        m_out.println(s);
        m_out.flush();
      } catch (Exception e) {
        m_ok = false;
      }
    }
  }

  // ==========================================================================
  // Attempt to get a string from the client. Returns NULL if there is
  // no string waiting to be read or if the connection is lost.
  // ==========================================================================
  public String get() {
    String got = null;

    // Do nothing is the Socket is not OK
    if (m_ok) {
      try {
        got = m_in.readLine();
        //
        // If readLine returns a null then this is another
        // indication that the socket is lost. Java either
        // throws an exception or returns null depending on the
        // exact circumstances (and it's virtual bit machine specific too)
        // so if we get a null we also unset OK
        if (got == null) {
          m_ok = false;
        }
      } catch (InterruptedIOException e) {
        // If nothing to read then we'll just return a null
        // all other exceptions results in us assuming that
        // the connection is lost and OK become unset.
      } catch (Exception e) {
        m_ok = false;
      }
    }
    return got;
  }

  // ==========================================================================
  // Gets a string and blocks until the string is returned
  // or the connection fails. This simply loops awaiting a reply
  // or returns null if the connection is lost
  // ==========================================================================
  public String get_blocking() {
    String s;
    while ((s = get()) == null && ok()) {
      // Nothing yet, give up our time slice
      Thread.yield();
    }
    // A null string indicates that the connection is lost
    return s;
  }

  // ==========================================================================
  // We have finished with the socket. Close it down nicely
  // ==========================================================================
  public void close() {
    try {
      m_sock.close();
    } catch (Exception e) {
    }
    m_ok = false;
  }

  // ==========================================================================
  // Is the connection still OK ?. final allows the compiler to inline
  // ==========================================================================
  public final boolean ok() {
    return m_ok;
  }

  // ==========================================================================
  // The member data, all private
  // ==========================================================================
  private boolean m_ok;
  private Socket m_sock;
  private BufferedReader m_in;
  private PrintWriter m_out;
}
